package zjut.raymond.controller;

import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.google.common.collect.ArrayListMultimap;
import com.google.common.collect.Multimap;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Controller;
import org.springframework.util.ClassUtils;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import zjut.raymond.algorithm.WifiFeatures;
import zjut.raymond.common.ApiResult;
import zjut.raymond.common.CommonUtils;
import zjut.raymond.common.ResultUtils;
import zjut.raymond.pojo.*;
import zjut.raymond.service.*;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.*;

/**
 * @author Raymond
 */
@Controller
@RequestMapping("/collector")
public class CollectorController {
    Logger logger = LoggerFactory.getLogger(CollectorController.class);
    @Autowired
    private RegionService regionService;
    @Autowired
    private ReferencePointService referencePointService;
    @Autowired
    private WifiDataService wifiDataService;
    @Autowired
    private WifiFingerprintService wifiFingerprintService;
    @Autowired
    private AccessPointService accessPointService;

    private static final int AP_NUMS = 8;

    /**
     * Get map by region id byte [ ]
     *
     * @param regionId region id
     * @return the byte []
     */
    @ResponseBody
    @RequestMapping(value = "/getMap", produces = MediaType.IMAGE_JPEG_VALUE)
    public byte[] getMapByRegionId(@RequestParam("regionId") Integer regionId) throws FileNotFoundException {
        QueryWrapper<Region> regionQueryWrapper = new QueryWrapper<>();
        regionQueryWrapper.select("map_uri").lambda().eq(Region::getId, regionId);
        Map<String, Object> map = regionService.getMap(regionQueryWrapper);

        byte[] bytes = null;
        try {
            String path = ClassUtils.getDefaultClassLoader().getResource("static/images").getPath();
            String filename = map.get("map_uri").toString();
            System.out.println(path + "/" + filename);
            File file = new File(path, filename);
            if (!file.exists()) {
                return null;
            }
            FileInputStream in = new FileInputStream(file);
            bytes = in.readAllBytes();
            in.close();
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return bytes;
    }

    @ResponseBody
    @RequestMapping(value = "/getRps", produces = "application/json")
    public ApiResult getAllRp() {
        List<Map<String, Object>> referencePointMaps = referencePointService.listMaps();
        return ResultUtils.successResult(referencePointMaps);
    }

    @ResponseBody
    @RequestMapping(value = "/addRP", method = RequestMethod.POST)
    public ApiResult addReferencePoint(@RequestParam("referencePointJson") String referencePointJson) {
        ReferencePoint referencePoint = JSON.parseObject(referencePointJson, ReferencePoint.class);
        int insert = referencePointService.getBaseMapper().insert(referencePoint);
        if (insert > 0) {
            return ResultUtils.successResult();
        } else return ResultUtils.errorResult();
    }

    @RequestMapping(value = "/uploadWifiData", method = RequestMethod.POST)
    public String collectWifiData(@RequestParam("rp_id") Integer referencePointId, @RequestParam("wifi_data") String apData) {
        //Map<String, Object> wifiMap = (Map<String, Object>) JSON.parse(apData);
        //   logger.info("id:{},wifi_data:{}", referencePointId, apData);
        Date date = new Date();
        //将原始wifi数据添加到tb_wifi_data表中
        WifiData data = new WifiData();
        data.setReferencePointId(referencePointId);
        data.setApData(apData);
        data.setUpdateTime(date);
        wifiDataService.save(data);

        //更新tb_reference_point 对应参考点更新时间字段
        UpdateWrapper<ReferencePoint> updateWrapper = new UpdateWrapper<>();
        updateWrapper.eq("id", referencePointId).set("update_time", date);
        referencePointService.getBaseMapper().update(null, updateWrapper);

        //转发
        return "/collector/process";
    }

    @ResponseBody
    @RequestMapping(value = "/process")
    public ApiResult processWifiData(@RequestParam("rp_id") Integer referencePointId) {
        //logger.info("process id:{}", referencePointId);
        //从AP表中选择可以的AP
        HashSet<String> apMacSet = selectAccessPointMacs(accessPointService);

        //从原始数据库表中选择参考点对应的原始wifi数据
        QueryWrapper<WifiData> wifiDataQueryWrapper = new QueryWrapper<>();
        wifiDataQueryWrapper.select("ap_data").eq("reference_point_id", referencePointId)
                .orderByDesc("id").last("limit 20");
        List<Object> objectList = wifiDataService.getBaseMapper().selectObjs(wifiDataQueryWrapper);
        //对应参考点的原始wifi数据数量
        Integer count = wifiDataService.getBaseMapper().selectCount(wifiDataQueryWrapper);
        count = count > 20 ? 20 : count;
        //  logger.info("wifidata:{}", objectList);
        //使用MultiMap数据结构将原始数据存起来
        Multimap<String, Integer> listMultimap = ArrayListMultimap.create();
        for (Object data : objectList) {
            Map<String, Integer> dataMap = (Map<String, Integer>) JSON.parse(String.valueOf(data));
            dataMap.keySet().retainAll(apMacSet);
            for (String key : dataMap.keySet()) {
                listMultimap.put(key, dataMap.get(key));
            }
        }
        //  logger.info("Mutlimap:{}", listMultimap);
        //JSONArray jsonArray = new JSONArray();
        List<WifiFeatures> wifiFeaturesList = new ArrayList<>();
        List<WifiFeatures> subList = new ArrayList<>();
        for (String key : listMultimap.keySet()) {
            Collection<Integer> integers = listMultimap.get(key);
            //计算丢失率
            double loseRate = 1 - ((double) integers.size() / count);
            if (loseRate <= 0.3) {
                //丢失小于等于0.3时计算均值和样本方差
                double mean = CommonUtils.getMean(integers);
                double std = CommonUtils.getStdDev(integers);
                //   logger.info("key:{},lr:{},value:{},mean:{},Std:{}", key, loseRate, integers, mean, std);
                // JSONObject jsonObject = new JSONObject();
                // jsonObject.put("ap_mac", key);
                // jsonObject.put("mean", mean);
                // jsonObject.put("standardDeviation", std);
                //jsonArray.add(JSON.toJSON(new WifiFeatures(key, mean, std)));
                wifiFeaturesList.add(new WifiFeatures(key, mean, std));
            }
        }
        Collections.sort(wifiFeaturesList);
        // logger.info("wifiFeaturesList:{}", JSON.toJSONString(wifiFeaturesList));
        UpdateWrapper<WifiFingerprint> wifiFingerprintUpdateWrapper = new UpdateWrapper<>();
        wifiFingerprintUpdateWrapper.eq("reference_point_id", referencePointId);
        if (wifiFeaturesList.size() > AP_NUMS) {
            subList = wifiFeaturesList.subList(0, AP_NUMS);
            //  logger.info("subList:{}", JSON.toJSONString(subList));
            wifiFingerprintService.saveOrUpdate(new WifiFingerprint(JSON.toJSONString(subList), referencePointId, new Date()),
                    wifiFingerprintUpdateWrapper);
        } else {
            wifiFingerprintService.saveOrUpdate(new WifiFingerprint(JSON.toJSONString(wifiFeaturesList), referencePointId, new Date()),
                    wifiFingerprintUpdateWrapper);
        }
        return ResultUtils.successResult();
    }

    static HashSet<String> selectAccessPointMacs(AccessPointService accessPointService) {
        QueryWrapper<AccessPoint> accessPointQueryWrapper = new QueryWrapper<>();
        accessPointQueryWrapper.select("ap_mac");
        List<Object> accessPointList = accessPointService.getBaseMapper().selectObjs(accessPointQueryWrapper);
        HashSet<String> apMacSet = new HashSet<>();
        accessPointList.forEach((mac) -> {
            apMacSet.add((String) mac);
        });
        return apMacSet;
    }

    @ResponseBody
    @RequestMapping(value = "/uniqueAP")
    public ApiResult uniqueAP() {
        HashSet<String> aps = new HashSet<>();
        QueryWrapper<WifiFingerprint> wrapper = new QueryWrapper<>();
        wrapper.select("ap_rssi_avg_dev");
        List<WifiFingerprint> fingerprints = wifiFingerprintService.list(wrapper);
        fingerprints.forEach((list) -> {
            list.getApRssiAvgDev();
            List<WifiFeatures> wifiFeaturesList = JSON.parseArray(list.getApRssiAvgDev(), WifiFeatures.class);
            wifiFeaturesList.forEach((o) -> {
                aps.add(o.getApMac());
            });
        });
        logger.info("ap_mac:{}", aps);
        return ResultUtils.successResult(aps);
    }
}
